#pragma once
#include "PDTStatement.h"
#include "PDTVar.h"
#include "PDTExp.h"
#include "PDTVecConst.h"
#include "PDTNumConst.h"
#include "PDTBoolConst.h"
#include "PDTSequenceOp.h"
#include "PDTSequenceVar.h"
#include "PDTNumVar.h"
#include "PDTBoolVar.h"

class PDTAssign :  public PDTStatement, public TraverseIfc <PDTAssign, PDTNode> {
    public:  
        TRAVERSAL_IFC_IMPL(PDTAssign)
public:
    bool isReturnStatement;          

	void setTarget(PDTVar &target);
	void setExpression(PDTExp &expression);
	void replaceExpression (PDTExp &expression);

	PDTVar * getTarget()const;
	PDTExp * getExpression()const;

	PDTAssign(PDTCodeGraph & CG, PDTVar &target, PDTExp &expression,
            bool aIsReturn=false);
	NodeTypeEnum getType() {
		return PDTAssign_Em;
	}
	bool isAssign() {
		return true;
	}

    bool isReturnAssignStatement() const {
        return isReturnStatement;}

    virtual bool SMT2visit(PDTSMT2Visitor & vis);

	virtual bool eval(PDTCodeGraph &CG);

	virtual PDTNode* clone(PDTCodeGraph &CG)
	{
		PDTNode* toReturn;
		PDTVar* targetClone = NULL;
		if (children[0]->isNumVar())
			targetClone = (PDTNumVar*)(children[0]->clone(CG));
		else if (children[0]->isBoolVar())
			targetClone = (PDTBoolVar*)(children[0]->clone(CG));
		PDTExp* expClone = (PDTExp*)(children[1]->clone(CG));
		toReturn = new 	PDTAssign(CG, *targetClone, *expClone);
		return toReturn;
	}

	virtual stringstream & toString(stringstream & ss) const {
		PDTVar * var = getTarget();
		PDTNode * node = var->getNode();
		node->toString(ss);
		ss << " := ";

		node = getExpression();
		node->toString(ss);
		return ss;
	}

};

class PDTPiAssign :  public PDTStatement, public TraverseIfc <PDTPiAssign, PDTNode> {
public:
    TRAVERSAL_IFC_IMPL(PDTPiAssign)
    
	void setTarget(PDTVar &target);
	void setExpression(PDTExp &expression);
	void replaceExpression (PDTExp &expression);

	PDTVar * getTarget()const;
	PDTExp * getExpression()const;

	PDTPiAssign(PDTCodeGraph & CG, PDTVar &target);

	NodeTypeEnum getType() {
		return PDTAssign_Em;
	}

    virtual bool isPiAssign() {
        return true;
    }

    virtual stringstream & toString(stringstream & ss) const {
        PDTVar * var = getTarget();
        PDTNode * node = var->getNode();

        node->toString(ss);
        ss << " := primary inputs ";
        return ss;
    }
};

class PDTSequenceAssign :  public PDTStatement, public TraverseIfc <PDTSequenceAssign, PDTNode> { public:  TRAVERSAL_IFC_IMPL(PDTSequenceAssign)
public:
	PDTVar * getTarget()const;
	PDTConstSequence * getSequenceConst()const;

	PDTSequenceAssign(PDTCodeGraph & CG, PDTVar &target, PDTConstSequence &expression);
	NodeTypeEnum getType() {
		return PDTAssign_Em;
	}

	bool isAssign() {
		return true;
	}

	bool isSequenceAssign() {
		return true;
	}

    virtual bool eval(PDTCodeGraph &CG)
    {
    	PDTSequenceVar* tarVar = dynamic_cast<PDTSequenceVar*>(getTarget());
    	//PDTSequence* tar = tarVar->getSequence();
    	PDTConstSequence* expr = getSequenceConst();
    	int size = expr->getSize();
    	for (int i = 0; i<size; i++) //for all sequence elements
    	{
    		PDTNode* elem = expr->getElementAt(i);
    		elem->eval(CG); //get the element at i and evaluate it
    		if (elem->isNumConstInt())
    		{
    			PDTNumConstInt* toAssign = dynamic_cast<PDTNumConstInt*>(elem);
				PDTNumConstInt ind(CG, i);
				PDTSequenceAccessNum atInd(CG, *tarVar, ind); //create a sequence access operator
				PDTAssign assAtInd(CG, atInd, *toAssign); //assign it
				assAtInd.eval(CG); //evaluate the assignment
    		}
    		else if (elem->isBoolConst())
    		{
    			PDTBoolConst* toAssign = dynamic_cast<PDTBoolConst*>(elem);
    			PDTNumConstInt ind(CG, i);
				PDTSequenceAccessNum atInd(CG, *tarVar, ind);
				PDTAssign assAtInd(CG, atInd, *toAssign);
				assAtInd.eval(CG);
    		}
    		else if (elem->isUnaryNumExpr()) //if have -1 for example, evaluate first
    		{
    			PDTNumConstInt* toAssign = dynamic_cast<PDTNumConstInt*>(elem->nValue);
    			PDTNumConstInt ind(CG, i);
				PDTSequenceAccessNum atInd(CG, *tarVar, ind);
				PDTAssign assAtInd(CG, atInd, *toAssign);
				assAtInd.eval(CG);
    		}
    	}
    	tarVar->nValue = expr;
    	return true;
    }

	virtual PDTNode* clone(PDTCodeGraph &CG)
	{
		PDTNode* toReturn;
		PDTVar* targetClone = NULL;
		if (children[0]->isSequenceVar())
			targetClone = (PDTSequenceVar*)(children[0]->clone(CG));
		PDTConstSequence* expClone = getSequenceConst();
		toReturn = new 	PDTSequenceAssign(CG, *targetClone, *expClone);
		return toReturn;
	}

	virtual stringstream & toString(stringstream & ss) const {
		PDTVar * var = getTarget();
		PDTNode * node = var->getNode();
		node->toString(ss);
		ss << " := ";

		node = getSequenceConst();
		node->toString(ss);

		return ss;
	}

};
